package com.qianniu.napi.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.jpay.ext.kit.PaymentKit;
import com.jpay.weixin.api.WxPayApi;
import com.jpay.weixin.api.WxPayApiConfig;
import com.qianniu.napi.admin.entity.*;
import com.qianniu.napi.admin.entity.penum.IotypeEnum;
import com.qianniu.napi.admin.entity.penum.JftypeEnum;
import com.qianniu.napi.admin.entity.penum.XftypeEnum;
import com.qianniu.napi.admin.service.*;
import com.qianniu.napi.common.config.Constants;
import com.qianniu.napi.common.util.*;
import org.jdom.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

@Service
public class QnpayImpl implements IQnpay {

    private static final Logger logger = LoggerFactory.getLogger(QnpayImpl.class);

    @Autowired
    private IMerchantService merchantService;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private IAccountFlowService accountFlowService;
    @Autowired
    private IMaccountService maccountService;
    @Autowired
    private IMaccountFlowService maccountFlowService;
    @Autowired
    private IGoodsService goodsService;
    @Autowired
    private IOrderService orderService;
    @Autowired
    private IJifenFlowService jifenFlowService;
    @Autowired
    private IOrderRefundService orderRefundService;
    @Autowired
    private IUserService userService;
    @Autowired
    private IRedisService redisService;
    @Autowired
    private IOrderGoodsService orderGoodsService;
    @Autowired
    private IJifenService jifenService;
    @Autowired
    private IXnbiFlowService xnbiFlowService;
    @Autowired
    private IXnbiService xnbiService;

    //订单取消只限在核销前或者用户确认收货前
    //一旦核销或者确认收货，不在之前该接口取消订单，
    //一旦核销或者确认收货，仅支持线下退货线下退款
    public Map cancelOrder(Order order, String appid) {

        //1、退现金
        Map map = null;
        if (order.getRealpay().doubleValue() > 0) {
            map = getRefundmap(order.getMpid(), appid, order.getOrderNo(), order.getRefundNo(),
                    order.getRealpay(), order.getRealpay(), Constants.REFUND_SOURCE_UNSETTLED_FUNDS);

            if (map == null || !map.containsKey("result_code") || !map.get("result_code").equals("SUCCESS")) {
                map.put("code", 1);
                map.put("msg", "weixin refund error");
                return map;
            }
        }

        //********** 写入[用户退款流水] START **********
        //========== 2、退余额:  ============
        // 全部在线全额退款，用户账户表无变化
        // 部分使用会员余额支付，用户账户有变化
        Long uid = order.getUid();
        Long mid = order.getMid();
        String sn = order.getOrderNo();
        Boolean flag = false;
        AccountFlow af = null;
        Account naccount = null;
        BigDecimal zero = new BigDecimal(0);

        OrderRefund orf = orderRefundService.selectOne(new EntityWrapper<OrderRefund>().eq("oid", order.getId()));
        if (orf == null) {
            map.put("code", 4);
            map.put("msg", "退款订单未生成");
            return map;
        }

        List<AccountFlow> afs = accountFlowService.selectList(new EntityWrapper<AccountFlow>()
                .eq("sn", order.getOrderNo()).eq("is_confirm", 1));
        for (AccountFlow oaf : afs) {
            //处理用户消费购物退款
            if (oaf.getXftype().intValue() == XftypeEnum.buy.getVal()) {

                af = new AccountFlow();
                // 总价 300 券 200 （余额99.9=会员余额49.9+赠送余额50 ）+实付0.1
                af.setRealpay(oaf.getRealpay());//0.1
                af.setDikou(oaf.getDikou());//200+50
                af.setAmount(oaf.getAmount());//49.9

                af.setTotal(af.getRealpay().add(af.getAmount()).add(af.getDikou()));
                af.setIsConfirm(-1);
                af.setRemark("订单取消退款");
                af.setState(1);

                af.setUid(oaf.getUid());
                User user = userService.selectById(oaf.getUid());
                af.setHeadimg(user.getAvatarPic());
                af.setPhone(user.getPhone());
                af.setNickname(user.getNickName());

                af.setMid(oaf.getMid());
                af.setMpid(oaf.getMpid());
                af.setTradetime(new Date());
                af.setCreatetime(new Date());
                af.setConfirmDate(new Date());
                af.setIotype(IotypeEnum.output.getVal());
                af.setXftype(XftypeEnum.refund.getVal());
                af.setTransactionid(null);

                af.setSn(orf.getRefundNo());
                af.insert();

                naccount = new Account();

                BigDecimal ambc = oaf.getAmount();
                BigDecimal mkbc = oaf.getDikou().subtract(order.getDiscount());// 250-200

                while (af != null && !flag) {
                    Account account = accountService.selectOne(new EntityWrapper<Account>().eq("uid", af.getUid()));
                    if (account == null) {
                        logger.error(">>>account at uid=" + uid + " not exist and sn=" + sn);
                        break;
                    }
                    af.setIsConfirm(1);
                    af.setLasttotal(account.getTotal());
                    af.setConfirmDate(new Date());

                    Long version = account.getVersion();
                    BigDecimal amount = account.getAmount();
                    BigDecimal total = account.getTotal();
                    BigDecimal mamount = account.getMamount();

                    //实付钱已退款，只更新余额和营销金额
                    naccount.setTotal(total.add(ambc).add(mkbc));
                    naccount.setAmount(amount.add(ambc));
                    naccount.setMamount(mamount.add(mkbc));

                    naccount.setModifytime(new Date());
                    naccount.setModifier(uid);
                    naccount.setVersion(version + 1);

                    flag = accountService.update(naccount, new EntityWrapper<Account>().eq("uid", af.getUid()).eq("version", version));
                    if (flag) af.insertOrUpdate();
                }
            } else if (oaf.getXftype().intValue() == XftypeEnum.sell.getVal() ||
                    oaf.getXftype().intValue() == XftypeEnum.xffanli.getVal()) {
                //处理商家卖货退款
                af = oaf;
                af.setId(null);

                af.setTradetime(new Date());
                af.setCreatetime(new Date());
                af.setIotype(IotypeEnum.output.getVal());
                af.setXftype(XftypeEnum.refund.getVal());
                af.setRemark("订单取消退款");

                af.setIsConfirm(-1);
                af.setTransactionid(null);

                af.setSn(orf.getRefundNo());

                af.insert();

                naccount = new Account();

                BigDecimal ambc = oaf.getAmount();

                while (af != null && !flag) {
                    Account account = accountService.selectOne(new EntityWrapper<Account>().eq("uid", af.getUid()));
                    if (account == null) {
                        logger.error(">>>account at uid=" + uid + " not exist and sn=" + sn);
                        break;
                    }
                    af.setIsConfirm(1);
                    af.setLasttotal(account.getTotal());
                    af.setConfirmDate(new Date());

                    Long version = account.getVersion();

                    BigDecimal total = account.getTotal();
                    BigDecimal unuseAmount = account.getUnuseAmount();
                    BigDecimal fanliing = account.getFanliing();

                    //实付钱已退款，只更新余额和营销金额
                    naccount.setTotal(total.subtract(ambc));
                    naccount.setUnuseAmount(unuseAmount.subtract(af.getAmount()));
                    naccount.setFanliing(fanliing.subtract(af.getAmount()));

                    naccount.setModifytime(new Date());
                    naccount.setModifier(uid);
                    naccount.setVersion(version + 1);

                    flag = accountService.update(naccount, new EntityWrapper<Account>().eq("uid", af.getUid()).eq("version", version));
                    if(flag)af.insertOrUpdate();
                }
            }
        }

        flag = false;
        // 3、退积分
        List<JifenFlow> jifenFlows = jifenFlowService.selectList(new EntityWrapper<JifenFlow>().eq("sn", order.getOrderNo()));
        for (JifenFlow jf : jifenFlows) {
            if (jf != null) {
                jf.setId(null);

                jf.setJftype(JftypeEnum.refund.getVal());//积分退款
                jf.setIotype(jf.getIotype() * -1);//积分
                jf.setConfirmDate(new Date());
                jf.setCreatetime(new Date());
                jf.setSn(order.getRefundNo());
                jf.setRemark("订单取消积分退款");
                jf.insert();
            }
            Jifen njifen = null;
            while (jf != null && !flag) {
                Jifen jifen = jifenService.selectOne(new EntityWrapper<Jifen>().eq("uid", jf.getUid()));
                njifen = new Jifen();

                long version = jifen.getVersion();
                njifen.setTotal(jf.getIotype() == 1 ? jifen.getTotal().add(jf.getNum()) : jifen.getTotal().subtract(jf.getNum()));
                njifen.setModifytime(new Date());
                njifen.setModifier(ObjectUtil.getUid());
                njifen.setVersion(version + 1);

                flag = jifenService.update(njifen, new EntityWrapper<Jifen>().eq("uid", jf.getUid()).eq("version", version));
            }
        }


        // 4、退优惠券【一经使用，一概不退】

        // 5、退虚拟币
        List<XnbiFlow> xnbiFlows = xnbiFlowService.selectList(new EntityWrapper<XnbiFlow>()
                .eq("sn", order.getOrderNo()));
        for (XnbiFlow xflow : xnbiFlows) {
            if (xflow != null) {
                xflow.setId(null);

                xflow.setJftype(JftypeEnum.refund.getVal());//积分退款
                xflow.setIotype(xflow.getIotype() * -1);//积分
                xflow.setCreatetime(new Date());
                xflow.setConfirmDate(new Date());
                xflow.setSn(order.getRefundNo());
                xflow.setRemark("订单取消虚拟币退款");
                xflow.insert();
            }

            Xnbi nxnbi = null;
            flag = false;
            while (xflow != null && !flag) {
                Xnbi xnbi = xnbiService.selectOne(new EntityWrapper<Xnbi>().eq("uid", xflow.getUid()));
                nxnbi = new Xnbi();

                long version = xnbi.getVersion();
                nxnbi.setTotal(xflow.getIotype() == 1 ? xnbi.getTotal().add(xflow.getNum()) : xnbi.getTotal().subtract(xflow.getNum()));
                nxnbi.setModifytime(new Date());
                nxnbi.setModifier(ObjectUtil.getUid());
                nxnbi.setVersion(version + 1);

                flag = xnbiService.update(nxnbi, new EntityWrapper<Xnbi>().eq("uid", xflow.getUid()).eq("version", version));
            }
        }

        //******* 写入[商家退款流水] *******
        MaccountFlow omaf = maccountFlowService.selectOne(new EntityWrapper<MaccountFlow>().eq("sn", order.getOrderNo()));

        MaccountFlow maf = new MaccountFlow();
        maf.setAmount(omaf.getAmount());
        maf.setMamount(omaf.getMamount());

        maf.setJifen(omaf.getJifen());
        maf.setIsConfirm(1);
        maf.setConfirmDate(new Date());
        maf.setCreatetime(new Date());
        maf.setTradetime(new Date());
        maf.setIotype(IotypeEnum.output.getVal());
        maf.setXftype(XftypeEnum.refund.getVal());
        maf.setSn(order.getRefundNo());
        maf.setMid(mid);
        maf.setMpid(order.getMpid());
        maf.setUid(uid);
        maf.insert();

        flag = false;
        if (maf != null) {
            flag = false;
            Maccount maccount = new Maccount();
            while (!flag) {
                Maccount ma = maccountService.selectOne(new EntityWrapper<Maccount>().eq("mid", mid));

                Long version = ma.getVersion();
                BigDecimal amount = ma.getAmount();
                BigDecimal total = ma.getTotal();
                BigDecimal sale = ma.getSaleAmount();
                BigDecimal market = ma.getMamount();

                maccount.setTotal(total.subtract(maf.getAmount()));        // 总金额
                maccount.setSaleAmount(sale.subtract(maf.getAmount()));    // 累计销售额度
                maccount.setAmount(amount.subtract(maf.getAmount()));      // 可用余额
                maccount.setAmount(market.subtract(maf.getMamount()));     // 营销总金额

                maccount.setModifytime(new Date());
                maccount.setModifier(ObjectUtil.getUid());
                maccount.setVersion(version + 1);

                flag = maccountService.update(maccount, new EntityWrapper<Maccount>().eq("id", ma.getId()).eq("version", version));
            }
        }

        return map;
    }

    public Boolean updateOrderStatus(Order o, int status) {

        // 更新订单+订单商品状态：已取消
        OrderGoods og = new OrderGoods();
        og.setStatus(status);
        og.setModifytime(new Date());
        if (o.getTransactionId() != null) og.setTransactionId(o.getTransactionId());
        og.setModifier(o.getUid());
        if (o.getRefundNo() != null) og.setRefundNo(o.getRefundNo());
        logger.info("=======updateOrderStatus 111 =", JSON.toJSONString(o));

        orderGoodsService.update(og, new EntityWrapper<OrderGoods>().eq("oid", o.getId()));
        if (status == 2) {
            // ====== 待完善 ======
            // ====== 根据各种类型商品进行状态值变更 ======

        }
        logger.info("=======updateOrderStatus 222 =", JSON.toJSONString(o));

        o.setStatus(status);// 已取消
        if (o.getTransactionId() != null) o.setTransactionId(o.getTransactionId());
        o.setModifytime(new Date());
        o.setModifier(o.getUid());
        return o.updateById();
    }

    /*
     * 企业付款到零钱
     */
    public Map payPakect(Long mpid, String appid, User user, BigDecimal money, String desc, String ip) {

        MerchantApp mapp = redisService.getMApp(mpid);
        int total = money.multiply(new BigDecimal(100)).intValue();

        String mchid = mapp.getMchid();
        Map<String, String> params = new HashMap<String, String>();
        params.put("mch_appid", appid);
        params.put("mchid", mchid);
        String nonceStr = String.valueOf(System.currentTimeMillis());
        params.put("nonce_str", nonceStr);
        String partnerTradeNo = String.valueOf(System.currentTimeMillis());
        params.put("partner_trade_no", partnerTradeNo);
        params.put("openid", user.getOpenid());
        params.put("check_name", "NO_CHECK");
        params.put("amount", String.valueOf(total));
        params.put("desc", desc);
        params.put("spbill_create_ip", ip);

        // API-KEY
        params.put("sign", PaymentKit.createSign(params, mapp.getPaykey()));

        String certPath = Constants.CERT_ROOT_PATH + mapp.getMcode() + "/" + mapp.getMpcode() + "/apiclient_cert.p12";


        logger.debug(">>>params=" + JSON.toJSONString(params));
        logger.debug(">>>certPath=" + certPath);
        logger.debug(">>>mchid=" + mchid);

        // 提现
        String transfers = WxPayApi.transfers(params, certPath, mchid);

        Map<String, String> map = PaymentKit.xmlToMap(transfers);
        logger.debug(">>>map=" + map);

        return map;
    }

    /*
     * 企业付款到零钱
     */
    public Map payBank(Long mid, String appid, String openId, BigDecimal money, String desc, String ip) {

        Map map = new HashMap<String, String>();
        MerchantApp merchant = merchantService.getCurrentMerchant(mid, appid);

        Map<String, String> params = new HashMap<String, String>();
        params.put("mch_appid", appid);
        params.put("mchid", merchant.getMchid());
        String nonceStr = String.valueOf(System.currentTimeMillis());
        params.put("nonce_str", nonceStr);
        String partnerTradeNo = String.valueOf(System.currentTimeMillis());
        params.put("partner_trade_no", partnerTradeNo);
        params.put("openid", openId);
        params.put("check_name", "NO_CHECK");
        params.put("amount", "100");
        params.put("desc", desc);

//        String ip = IpKit.getRealIp(getRequest());
//        if (StrKit.isBlank(ip)) {
//            ip = "127.0.0.1";
//        }
//        params.put("spbill_create_ip", ip);
//
//        params.put("sign", PaymentKit.createSign(params, partnerKey));
//        System.out.println("certPath>"+certPath);
//        // 提现
//        String transfers = WxPayApi.transfers(params, certPath, mch_id);
//
//        logger.info("提现结果:" + transfers);
//        System.out.println("提现结果:" + transfers);
//
//        Map<String, String> map = PaymentKit.xmlToMap(transfers);
//        String return_code = map.get("return_code");
//        String result_code = null;
//        if (("SUCCESS").equals(return_code)) {
//            result_code = map.get("result_code");
//            if (("SUCCESS").equals(result_code)) {
//                //提现成功
//            } else {
//                //提现失败
//            }
//        }
//        renderText(transfers);

        return map;
    }

    /**
     * 微信小程序支付
     */
    public Map minappPay(Long mid, String appid, Long uid, BigDecimal money, String desc, String sn, String ip) {

        int total = money.multiply(new BigDecimal(100)).intValue();
        Map<String, String> map = new HashMap<String, String>();
        User user = userService.selectById(uid);
        MerchantApp mapp = redisService.getMApp();
//        String notify = ismch?"/mrecharge/notify":"/recharge/notify";
        String notify = "/recharge/notify";
        String apikey = mapp.getPaykey();

        Map<String, String> params = WxPayApiConfig.New()
                .setAppId(appid)
                .setMchId(mapp.getMchid())
                .setPaternerKey(apikey)
                .setAttach(desc)
                .setBody(desc)
                .setSpbillCreateIp(ip)
                .setTotalFee(String.valueOf(total))
                .setTradeType(WxPayApi.TradeType.JSAPI)
                .setNotifyUrl(Constants.ROOT_URL + notify)
                .setOutTradeNo(sn)
                .setOpenId(user.getOpenid())
                .build();

        String xmlResult = WxPayApi.pushOrder(false, params);

        logger.info(xmlResult);
        Map<String, String> result = PaymentKit.xmlToMap(xmlResult);

        String return_code = result.get("return_code");
        String result_code = result.get("result_code");
        String return_msg = result.get("return_msg");
        if (!PaymentKit.codeIsOK(return_code) || !PaymentKit.codeIsOK(result_code)) {
            return result;
        }

        // 以下字段在return_code 和result_code都为SUCCESS的时候有返回
        String prepay_id = result.get("prepay_id");
        //封装调起微信支付的参数https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5

        map.put("appId", appid);
        map.put("timeStamp", System.currentTimeMillis() / 1000 + "");
        map.put("nonceStr", System.currentTimeMillis() + "");
        map.put("package", "prepay_id=" + prepay_id);
        map.put("signType", "MD5");

//        String sign = PayCommonUtil.createSign("UTF-8", params, apikey);//最后这个是自己设置的32位密钥
        String sign = PaymentKit.createSign(map, apikey);
        map.put("paySign", sign);
//        String jsonStr = JsonKit.toJson(packageParams);
//        logger.info("最新返回小程序支付的参数:"+jsonStr);
//        renderJson(jsonStr);
        return map;
    }

    /*
     *  申请退款
     *
     * @param mpid          商户应用ID
     * @param appid        分店appid
     * @param orderNo      商户订单号
     * @param refundNo     商户退款单号
     * @param realpay      订单金额
     * @param refund       退款金额
     * @param refundType   退款资金来源（默认传 "REFUND_SOURCE_UNSETTLED_FUNDS"）
     */
    public Map<String, Object> getRefundmap(Long mpid, String appid, String orderNo, String refundNo,
                                            BigDecimal realpay, BigDecimal refund, String refundType) {

        // ****** 生成支付相关字段 ******
        // 微信支付价格值单位为：分 1代表1分，所以传过来的值必须*100；
        int totalfee = realpay.multiply(new BigDecimal("100")).intValue();
        int refundfee = refund.multiply(new BigDecimal("100")).intValue();

        // 订单编号
        String title = "申请退费";//订单标题

        // 时间戳
        String nonce_str = StringUtil.getGUID().substring(0, 16);
        SortedMap<String, Object> params = new TreeMap<String, Object>();
        MerchantApp mapp = redisService.getMApp(mpid);

        // API-KEY
        String paykey = mapp.getPaykey();

        params.put("appid", mapp.getAppid());
        params.put("mch_id", mapp.getMchid());
        params.put("nonce_str", nonce_str);//  时间戳

        params.put("out_trade_no", orderNo);  // 交易编号
        params.put("out_refund_no", refundNo);// 退款编号
        params.put("total_fee", totalfee);//   总价格
        params.put("refund_fee", refundfee);// 退费价格
//        params.put("refund_account", refundType);//非必填： openid   // 仅针对老资金流商户使用

        //获取sign
        logger.debug(">>>>>>>>>>>>>>>params=" + JSON.toJSONString(params));
        logger.debug(">>>>>>>>>>>>>>>apikey=" + paykey);
        Map map = null;
        try {

            String sign = PayCommonUtil.createSign("UTF-8", params, paykey);//最后这个是自己设置的32位密钥
            logger.debug(">>>sign=" + sign);

            params.put("sign", sign);

            //转成XML
            String requestXML = PayCommonUtil.getRequestXml(params);
            String certPath = Constants.CERT_ROOT_PATH + mapp.getMcode() + "/" + mapp.getMpcode() + "/apiclient_cert.p12";

            //得到含有prepay_id的XML
            String resXml = null;
            try {
                logger.debug("==================requestXML=" + requestXML.toString());
                logger.debug("==================certPath=" + certPath);
                logger.debug("==================mapp=" + mapp.toString());
//                resXml = HttpUtil.refundPost(Constants.WX_URL_PAY_REFUND, requestXML, certPath, paykey);
                resXml = HttpUtil.refundPost(Constants.WX_URL_PAY_REFUND, requestXML, certPath, mapp.getMchid());
            } catch (Exception e) {
                logger.error(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>refundPost error", e);
            }

            logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>resXml from refundPost");
            logger.info(resXml);

            Map<String, Object> result = new HashMap<String, Object>();
            //解析XML存入Map

            try {
                map = XMLUtil.doXMLParse(resXml);
            } catch (JDOMException | IOException e) {
                logger.error(">>>doXMLParse error", e);
                result.put("code", 5);
                result.put("msg", "支付异常");
                return result;
            }
            if (map.get("result_code").equals("FAIL")) {
                result.put("code", 5);
                result.put("msg", map.get("err_code_des"));
                return result;
            }

            //未结算金额不足  使用余额退款
            if (map != null && map.get("result_code").equals("FAIL") &&
                    Constants.NOT_ENOUGH_MONEY.equals(map.get("err_code"))
                    && Constants.REFUND_SOURCE_UNSETTLED_FUNDS.equals(refundType)) {

                return getRefundmap(mpid, appid, orderNo, refundNo, realpay, refund, Constants.REFUND_SOURCE_RECHARGE_FUNDS);
            }
        } catch (Exception e) {
            logger.error("-------------------refund error", e);
        }
        logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>getRefundmap=" + JSON.toJSONString(map));
        return map;
    }
}
